package com.power.sifa.net.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.aventrix.jnanoid.jnanoid.NanoIdUtils;
import com.plf.common.domain.ResponseDTO;
import com.plf.common.exception.BusinessException;
import com.plf.common.utils.DateUtil;
import com.plf.common.utils.RestUtil;
import com.plf.common.utils.StringUtil;
import com.plf.common.utils.TrustAllHttpsInitializer;
import com.plf.core.base.service.Ha_formService;
import com.power.flowable.api.IPersonalFeign;
import com.power.flowable.model.Act_dsn_form_table;
import com.power.flowable.model.enumeration.Tp_tab;
import com.power.sifa.approve.dao.Apl_mainDao;
import com.power.sifa.approve.model.Apl_main;
import com.power.sifa.approve.model.enumeration.St_admreview;
import com.power.sifa.approve.model.enumeration.St_apply;
import com.power.sifa.net.formact.service.FormNextFlyweight;
import com.power.sifa.net.formact.service.FormNextService;
import com.power.sifa.net.model.Form_apply;
import com.power.sifa.net.model.ProcessInfo;
import com.power.sifa.net.model.enumeration.Tp_matter;
import com.power.sifa.net.service.ApplyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@Slf4j
public class ApplyServiceImpl implements ApplyService {

    @Resource
    private Apl_mainDao apl_mainDao;
    // flowable 业务接口地址
    @Value("${feign.client.flowable-feignurl:#{null}}")
    private String businessUrl;
    @Resource
    private Ha_formService ha_formService;
    // 自动提交机器userid
    @Value("${power.auto-submit-robot:#{null}}")
    private String autoSubmitRobot;
    @Resource
    private IPersonalFeign iPersonalFeign;

    @Override
    public void saveApplyForm(String formid, Form_apply form_apply) throws Exception {
//        Map<String, Object> result = new HashMap<>(16);
        // log.info(JSON.toJSONString(form_apply.getForm()));
        // 1. 保存数据表
        this.saveFormData(formid, form_apply.getForm(), form_apply);
    }

    private List<Act_dsn_form_table> findTableConfig(String formid) throws IOException, BusinessException {
        RestUtil.HttpClient client = RestUtil.getClient(this.businessUrl + "act_dsn_form_table/queryListByFormId").post()
                .addInitializer(new TrustAllHttpsInitializer());
        if (formid != null) {
            client.addQueryParam("formid", formid);
        }
        RestUtil.HttpResponse resp = client.request();
        JSONObject back = (JSONObject) resp.getContent();
        Integer code = (Integer) back.get("code");
        if (code != 0) {
            log.error("查询流程表单配置表失败 -> msg[{}]", back.get("errormsg"));
            throw new BusinessException((String) back.get("errormsg"));
        }
        List<Act_dsn_form_table> list = JSON.parseArray(JSON.toJSONString(back.get("data")), Act_dsn_form_table.class);
        return list;
    }


    private String saveFormData(String formid, Map<String, Object> formData, Form_apply form_apply) throws Exception {
        // 1. 保存表单数据(先主表, 后从表)
        // 1.1 查询当前表单的表关系
        List<Act_dsn_form_table> tableList = this.findTableConfig(formid);
        // 1.2 list转map
        Map<String, Act_dsn_form_table> tableMap = new HashMap<>(16);

        HashMap<String, Object> body = new HashMap<>(16);
        for (Act_dsn_form_table v : tableList) {
            tableMap.put(v.getTabname_(), v);
            // * 防止本次保存没有主表字段, 去找一遍, 给个初始值
            if (v.getType_().equals(Tp_tab.MAIN)) {
                Map<String, Object> masterMap = new HashMap<>(16);
                masterMap.put("table", v.getTabname_());
                masterMap.put("pk", v.getPrimarykey_());
                body.put("master", masterMap);
            }
        }
        // 主表
        List<Map<String, Object>> masterList = new ArrayList<>(10);
        // 继承主表
        List<Map<String, Object>> extendList = new ArrayList<>(10);
        // 引用主表
        List<Map<String, Object>> referList = new ArrayList<>(10);
        // 从表
        List<Map<String, Object>> subList = new ArrayList<>(10);
        // 中间map变量
        Map<String, Object> tempMap;
        Map<String, Object> tempModelMap;
        List<Map<String, Object>> tempModelList;

        // 1.3 数据组装
        for (Map.Entry<String, Object> entry : formData.entrySet()) {
            if (tableMap.get(entry.getKey()) != null) {
                Act_dsn_form_table table = tableMap.get(entry.getKey());

                tempMap = new HashMap<>(16);
                tempMap.put("table", table.getTabname_());
                tempMap.put("pk", table.getPrimarykey_());
                // 组装表属性值

                // * 从表的子表格数据是数组
                if (table.getType_().equals(Tp_tab.SUB)) {
                    tempModelList = new ArrayList<>(10);
                    Map<String, List<Map<String, Object>>> item = (Map<String, List<Map<String, Object>>>) entry
                            .getValue();
                    for (Map.Entry<String, List<Map<String, Object>>> itemEntry : item.entrySet()) {
                        tempModelMap = new HashMap<>(16);
                        List<Map<String, Object>> itemSon = (List<Map<String, Object>>) itemEntry.getValue();
                        for (Map<String, Object> v : itemSon) {
                            tempModelMap.put((String) v.get("field"), v.get("value"));
                        }
                        tempModelList.add(tempModelMap);
                    }

                    // * 从表类型是数组
                    tempMap.put("model", tempModelList);
                } else {
                    tempModelMap = new HashMap<>(16);
                    List<Map<String, Object>> item = (List<Map<String, Object>>) entry.getValue();
                    for (Map<String, Object> v : item) {
                        // * 表id不允许修改
                        tempModelMap.put((String) v.get("field"), v.get("value"));

                    }
                    // * 主表类型是对象
                    tempMap.put("model", tempModelMap);
                }

                // 判断表类型
                if (table.getType_().equals(Tp_tab.MAIN)) {
                    masterList.add(tempMap);
                } else if (table.getType_().equals(Tp_tab.INHERIT)) {
                    extendList.add(tempMap);
                } else if (table.getType_().equals(Tp_tab.REFER)) {
                    tempMap.put("fk", table.getForeignkey_());
                    referList.add(tempMap);
                } else if (table.getType_().equals(Tp_tab.SUB)) {
                    tempMap.put("fk", table.getForeignkey_());
                    subList.add(tempMap);
                }
            }

        }

        // 2. 调工具接口保存表数据
        if (!CollectionUtils.isEmpty(masterList)) {
            // 找一遍有没有申请编号 applycode, 没有给一个
            for (Map<String, Object> v : masterList) {
                if (((Map) v.get("model")).get("applycode") == null) {
                    ((Map) v.get("model")).put("applycode", this.generateApplyCode());
                }
            }
            // 主表只有一张
            body.put("master", masterList.get(0));
        }
        // 没有主表直接return
        if (body.get("master") == null) {
            log.error("无主表, 表单数据保存失败!");
            throw new RuntimeException("无主表, 表单数据保存失败!");
        }
        if (!CollectionUtils.isEmpty(extendList)) {
            // 找一遍有没有id, 没有给一个
            for (Map<String, Object> v : extendList) {
                if (((Map) v.get("model")).get("id") == null) {
                    ((Map) v.get("model")).put("id", NanoIdUtils.randomNanoId());
                }
            }
            body.put("extend", extendList);
        }
        if (!CollectionUtils.isEmpty(referList)) {
            // 找一遍有没有id, 没有给一个
            for (Map<String, Object> v : referList) {
                if (((Map) v.get("model")).get("id") == null) {
                    ((Map) v.get("model")).put("id", NanoIdUtils.randomNanoId());
                }
            }
            body.put("lookup", referList);
        }
        if (!CollectionUtils.isEmpty(subList)) {
            body.put("slave", subList);
        }
        log.info(JSON.toJSONString(body));

        // 获取表单的校验对象
        FormNextService formNextObj = FormNextFlyweight.getInstanse(form_apply.getMatid());
        // 3. 下一步操作
        if (form_apply.getIscheck()) {
            // 3.3 表单下一步之前操作
            if (null != formNextObj) {
                formNextObj.before(form_apply.getApplyid(), body);
            }
        } else {
            // 3.3 表单保存之前操作
            if (null != formNextObj) {
                formNextObj.save(form_apply.getApplyid(), body);
            }
        }
        // 4. 返回存储表保存后返回的主键id
        String bizid = this.formSave(body);
        if (form_apply.getIscheck() && null != formNextObj) {
            // 4.1 表单保存之后操作
            formNextObj.after(form_apply.getApplyid(), body);
        }
        if (form_apply.getIscheck() && null != formNextObj) {
            // 4.1 表单提交之后校验
            formNextObj.after(form_apply.getApplyid(), body);
        }

        return bizid;
    }

    /**
     * 保存表单数据
     *
     * @param body
     * @return
     * @throws Exception
     */
    private String formSave(Map<String, Object> body) throws Exception {
        Object result = this.ha_formService.saveFormData(body);
        return String.valueOf(String.valueOf(result));
    }

    //申请编号：数字5位, 不足补0
    private String APPLY_CODE_FMT = "%s%05d";

    @Override
    public String generateApplyCode() {
        String ymd = DateUtil.d2s_simple(new Date());
        Integer no = apl_mainDao.queryMaxno(ymd);
        AtomicInteger atomic = new AtomicInteger(no);
        String code = String.format(APPLY_CODE_FMT, ymd, atomic.incrementAndGet());
        return code;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitApply(String applyid) throws BusinessException {
        synchronized (applyid) {
            Apl_main mainObj = this.apl_mainDao.getById(applyid);

            FormNextService formNextObj = FormNextFlyweight.getInstanse(mainObj.getMatid());
            // 表单提交之前操作
            if (null != formNextObj) {
                formNextObj.submit(applyid, mainObj);
            }
            String now = DateUtil.dt2s(new Date());
            // 2.1 没有就设置提交时间
            if (null == mainObj.getSubtime()) {
                mainObj.setSubtime(now);
            }
            mainObj.setUpdatetime(now);
            // 2.2 有备份异常前的状态就取之前的状态
            if (null != mainObj.getStatuscp()) {
                mainObj.setStatus(mainObj.getStatuscp());
            } else {
                mainObj.setStatus(St_apply.COMMIT);
            }
            // 3. 没有实例id则启流程 (行政诉讼不起流程)
            if(Tp_matter.PUB_RESPOND.equals(mainObj.getMatid())) {
                // 不起流程直接结束
                mainObj.setStatus(St_apply.FINISH);
            } else if (null == mainObj.getInstid()) {
                ProcessInfo processInfo= this.initiateProcess(mainObj);
                mainObj.setInstid(processInfo.getProcessinstanceid());
                mainObj.setScatid(processInfo.getScatid());
            }

            this.apl_mainDao.save(mainObj);
        }
    }

    @Override
    public Map<String, Object> loadApplyForm(String formid, String applyid)
            throws Exception {
        // 1. 取到流程表单保存的applyid

        Map<String, Object> result = null;
        Map<String, Object> body = null;
        if (!StringUtil.isEmpty(applyid)) {
            body = new HashMap<>(16);
            body.put("id", String.valueOf(applyid));
            // 2. 查询当前表单的表关系
            List<Act_dsn_form_table> tableList = this.findTableConfig(formid);
            List<Map<String, Object>> extendList = new ArrayList<>(10);
            List<Map<String, Object>> lookupList = new ArrayList<>(10);
            List<Map<String, Object>> slaveList = new ArrayList<>(10);
            Map<String, Object> tempMap = null;
            for (Act_dsn_form_table v : tableList) {
                tempMap = new HashMap<>(16);
                // 判断表类型
                if (v.getType_().equals(Tp_tab.MAIN)) {
                    tempMap.put("table", v.getTabname_());
                    tempMap.put("pk", v.getPrimarykey_());
                    body.put("master", tempMap);
                    continue;
                } else if (v.getType_().equals(Tp_tab.INHERIT)) {
                    tempMap.put("table", v.getTabname_());
                    tempMap.put("pk", v.getPrimarykey_());
                    extendList.add(tempMap);

                } else if (v.getType_().equals(Tp_tab.REFER)) {
                    tempMap.put("table", v.getTabname_());
                    tempMap.put("pk", v.getPrimarykey_());
                    tempMap.put("fk", v.getForeignkey_());
                    lookupList.add(tempMap);

                } else if (v.getType_().equals(Tp_tab.SUB)) {
                    tempMap.put("table", v.getTabname_());
                    tempMap.put("pk", v.getPrimarykey_());
                    tempMap.put("fk", v.getForeignkey_());
                    slaveList.add(tempMap);
                }
            }

            if (!CollectionUtils.isEmpty(extendList)) {
                body.put("extend", extendList);
            }
            if (!CollectionUtils.isEmpty(lookupList)) {
                body.put("lookup", lookupList);
            }
            if (!CollectionUtils.isEmpty(slaveList)) {
                body.put("slave", slaveList);
            }
            log.info(JSON.toJSONString(body));
            result = this.formLoad(body);
        }

        return result;
    }

    @Override
    public Map<String, Object> formLoadField(String formid, String applyid) throws Exception {
        Map<String, Object> initialMap = this.loadApplyForm(formid, applyid);
        Map<String, Object> resultMap = new HashMap<>(16);
        for (Map.Entry<String, Object> entry : initialMap.entrySet()) {
            if (entry.getKey().equals("master")) {
                Map<String, Object> masterInfo = (Map<String, Object>) entry.getValue();
                String table = (String) masterInfo.get("table");
                Map<String, Object> masterModel = (Map<String, Object>) masterInfo.get("model");
                for (Map.Entry<String, Object> modelEntry : masterModel.entrySet()) {
                    resultMap.put(table + "_" + modelEntry.getKey(), modelEntry.getValue());
                }
            } else if (entry.getKey().equals("lookup") || entry.getKey().equals("extend")) {
                List<Map<String, Object>> otherList = (List<Map<String, Object>>) entry.getValue();
                for (Map<String, Object> otherInfo : otherList) {
                    String table = (String) otherInfo.get("table");
                    Map<String, Object> masterModel = (Map<String, Object>) otherInfo.get("model");
                    for (Map.Entry<String, Object> modelEntry : masterModel.entrySet()) {
                        resultMap.put(table + "_" + modelEntry.getKey(), modelEntry.getValue());
                    }
                }
            }
        }

        return resultMap;
    }

    /**
     * 提交启动流程
     * @param mainObj
     * @return
     * @throws BusinessException
     */
    private ProcessInfo initiateProcess(Apl_main mainObj) throws BusinessException {

        if (StringUtil.isEmpty(mainObj.getRegsectionid())) {
            throw new BusinessException("申请拟受理单位id不能为空");
        }
        if (StringUtil.isEmpty(mainObj.getFormid())) {
            throw new BusinessException("申请表单定义id不能为空");
        }
        if (StringUtil.isEmpty(mainObj.getReguserid())) {
            throw new BusinessException("录入人id不能为空");
        }
        String userid;
        if(!StringUtil.isEmpty(mainObj.getReguserid())) {
            userid = mainObj.getReguserid();
        } else {
            userid = this.autoSubmitRobot;
        }
        // 3. 启动流程
        ResponseDTO res = this.iPersonalFeign.saveStartFormData(mainObj.getId(), userid, mainObj.getFormid(), null, new HashMap<>(16));
        if (0 != res.getCode()) {
            log.error("启动流程失败 -> msg[{}]", res.getMsg());
            throw new BusinessException("启动流程失败 -> msg: " + res.getMsg());
        }
        ProcessInfo processObj = JSON.parseObject(JSON.toJSONString(res.getData()), ProcessInfo.class);

        // 行政复议的子流程提交后自动抢占
        if(Tp_matter.PUB_ADMREVIEW.equals(mainObj.getMatid()) && !StringUtil.isEmpty(mainObj.getReguserid())) {
            ResponseDTO resTask = this.iPersonalFeign.getTaskByInstid(processObj.getProcessinstanceid());
            if (0 != resTask.getCode()) {
                log.error("查询流程任务失败 -> msg[{}]", res.getMsg());
                throw new BusinessException("查询流程任务失败 -> msg: " + res.getMsg());
            }
            ResponseDTO resClaim = this.iPersonalFeign.claimTask((String) resTask.getData(), mainObj.getReguserid());
            if (0 != resClaim.getCode()) {
                log.error("录入人抢占任务失败 -> msg[{}]", res.getMsg());
                throw new BusinessException("录入人抢占任务失败 -> msg: " + res.getMsg());
            }
        }
        return processObj;
    }

    /**
     * 回显表单数据
     */
    private Map<String, Object> formLoad(Map<String, Object> struct) throws Exception {
        Object result = this.ha_formService.loadFormData(struct);
        // 1. 取出从表
        List<Map<String, Object>> slaveList = (List<Map<String, Object>>) ((Map<String, Object>) result).get("slave");
        if (!CollectionUtils.isEmpty(slaveList)) {
            // 2. 如果是产品从表, 根据状态进行排序
            for (Map<String, Object> slaveObj : slaveList) {
                List<Map<String, Object>> modelList = ((List<Map<String, Object>>) slaveObj.get("model"));
                if (!CollectionUtils.isEmpty(modelList)) {
                    // 2.1包含sortnum字段, 则根据该字段排序
                    if (null != modelList.get(0).get("sortnum")) {
                        Collections.sort(modelList, (o1, o2) -> (int) o1.get("sortnum") > (int) o2.get("sortnum") ? 1 : -1);
                    }
                }
            }
        }
        return (Map<String, Object>) result;
    }


}
